package eva.savant.business.contabilidad.service

import eva.savant.businessobject.cg.Credito
import eva.savant.businessobject.cg.Documento
import eva.savant.businessobject.cg.Ejercicio
import eva.savant.businessobject.cg.Flujocaja
import eva.savant.businessobject.cg.Partida
import eva.savant.businessobject.cg.Periodo
import eva.savant.businessobject.cg.Asiento
import eva.savant.businessobject.cg.Preasiento
import eva.savant.businessobject.gl.Persona
import eva.savant.core.service.TSEntity

import groovy.time.TimeCategory
import org.springframework.stereotype.Repository

@Repository(value = 'tsContabilidad')
public class  TSContabilidadImpl extends TSEntity implements TSContabilidad {

  public Asiento mergeAsiento(Asiento a) throws Exception {

    if(a.lsPartida.size() < 1) throw new Exception ('El detalle cuentas esta vacio')


    sociedadId= a.sociedadId


    a.periodo = get(Periodo,'codigo=?',a.fecha.format('yyMM'))

    if(a.periodo == null) throw new Exception ('No existe periodo contable ' + a.fecha.format('MM-yyyy'))

    if(a.periodo.estado == BAJA) throw new Exception ("Periodo contable ${a.fecha.format('MM-yyyy')} cerrado")


    for(x in a.lsPartida) {

    x.asiento         = a

    x.operacionId     = a.operacionId

    x.sociedadId      = a.sociedadId

    x.usuarioId       = a.usuarioId }


    merge(a)
  }

  public Flujocaja mergeFlujocaja(Flujocaja f) throws Exception {

    if(f.monto <= 0) { throw new Exception ('El monto debe ser mayor a cero') }

    f= merge(f)

    f.asiento= mergeAsiento(buildAsiento(f))

    return merge(f)
  }

  public Ejercicio mergeEjercicio(Ejercicio e) throws Exception {

    if(e.lsPeriodo.size() < 1) { throw new Exception ('El detalle de periodos esta vacio') }


    sociedadId= e.sociedadId


    def lsPeriodo= find(Periodo,'ejercicio.id=?', sociedadId + e.codigo)

    lsPeriodo?.each { it.operacionId = BAJA; merge(it) }

    lsPeriodo = e.lsPeriodo; e = merge(e)


    for(x in lsPeriodo) {

    x.id         = e.sociedadId + x.codigo

    x.ejercicio  = e

    x.operacionId= e.operacionId

    x.sociedadId = e.sociedadId

    x.usuarioId  = e.usuarioId

    merge(x) }


    return e
  }

  public Preasiento mergePreasiento(Preasiento p) throws Exception {

    for(x in p.lsPreasientoDetalle) {

    x.preasiento = p

    x.operacionId= p.operacionId

    x.sociedadId = p.sociedadId

    x.usuarioId  = p.usuarioId }


    return merge(p)
  }

  public Documento saveDocumento(Documento d) throws Exception {

    if(d.saldo > d.importe) throw new Exception ('El saldo es mayor al importe')

    def x= get(Documento,d.id); def pagado= x!=null? x.importe - x.saldo : 0

    d= mergeDocumento(d)

    restaCredito(d.persona,pagado)

    sumaCredito(d.persona,d.importe-d.saldo)

    d.asiento= mergeAsiento(buildAsientoPago(d))

    return merge(d)
  }

  public Documento mergeDocumento(Documento d) throws Exception {

    if(d.persona?.id == null) throw new Exception ('La persona del documento es invalido')

    if(d.referencia?.length() < 1) throw new Exception ('El documento de referencia es invalido')

    if(d.importe <= 0) throw new Exception ('El importe debe ser mayor a cero')

    if(d.creacion == null) throw new Exception ('La fecha de creacion es invalido')

    if(d.vencimiento == null) throw new Exception ('La fecha de vencimiento es invalido')


    def suma= 0

    for(x in d.lsDocumentoDetalle) {

    suma= suma + x.importe

    x.documento  = d

    x.operacionId= d.operacionId

    x.sociedadId = d.sociedadId

    x.usuarioId  = d.usuarioId }

    d.saldo= d.importe - suma


    return merge(d)
  }

  public void anularDocumento(Documento d) throws Exception {

    d= get(Documento, d.id); if(d == null) return

    //if(d.lsDocumentoDetalle.size() > 0) throw new Exception('El documento tiene detalle de pagos')

    d.operacionId= BAJA; merge(d)
  }

  public void valCredito(Persona p, Date d, BigDecimal monto) throws Exception {

    if(monto <= 0) return; sociedadId= p.sociedadId

    def c= get(Credito,'persona.id=?',p.id)

    if(c == null) throw new Exception('La persona no tiene linea de credito')

    def t = TimeCategory.minus(toStartDay(c.expiracion), toStartDay(d))

    if(t.days < 0) throw new Exception('La fecha de credito ha expirado el ' + toDate(c.expiracion.time, 'dd-MMM-yyyy'))

    if(c.disponible < monto) throw new Exception("El credito disponible= $c.disponible es menor al monto solicitado= $monto")
  }

  public Credito restaCredito(Persona p, BigDecimal monto) throws Exception {

    if(p == null || monto == null) return null; sociedadId= p.sociedadId

    def c= get(Credito,'persona.id=?',p.id)

    c.disponible= c.disponible - monto

    return merge(c)
  }

  public Credito sumaCredito(Persona p, BigDecimal monto) throws Exception {

    if(p == null || monto == null) return null; sociedadId= p.sociedadId

    def c= get(Credito,'persona.id=?',p.id)

    c.disponible= c.disponible + monto

    return merge(c)
  }

  public Asiento buildAsientoPago(Documento d) throws Exception {

    sociedadId= d.sociedadId

    def preasiento= get(Preasiento,d.tipo.startsWith('CXC')?PREASIENTO_DOC_CXC:PREASIENTO_DOC_CXP)

    def c= get(Credito,'persona.id=?',d.persona.id)


    def x= d.asiento==null? new Asiento() : d.asiento

    x.tipo       = preasiento.tipo

    x.fecha      = d.ultimopago

    x.detalle    = d.descripcion

    x.importe    = d.importe - d.saldo

    x.referencia = d.tipo + ' ' + d.id

    x.operacionId= d.operacionId

    x.sociedadId = d.sociedadId

    x.usuarioId  = d.usuarioId

    x.lsPartida= new ArrayList<Partida>()


    preasiento.lsPreasientoDetalle.eachWithIndex { it, i -> println 'preasiento documento cuenta: '+it.cuenta.nombre

    def p= new Partida()

    p.cuenta= i < preasiento.lsPreasientoDetalle.size()-1? it.cuenta : c.cuenta

    p.debe  = it.grupo.equalsIgnoreCase('DEBE')?  (it.porcentaje/100) * x.importe : 0

    p.haber = it.grupo.equalsIgnoreCase('HABER')? (it.porcentaje/100) * x.importe : 0


    x.lsPartida.add p }


    return x
  }

  public Asiento buildAsiento(Flujocaja f) throws Exception {

    sociedadId= f.sociedadId

    def preasiento= get(Preasiento,f.documento.equals('FACTURA')?PREASIENTO_EGR_FACTURA:PREASIENTO_EGR_RECIBO)


    def x= f.asiento == null? new Asiento() : f.asiento

    x.tipo       = preasiento.tipo

    x.fecha      = f.fecha

    x.detalle    = f.detalle

    x.importe    = f.monto

    x.referencia = f.referencia

    x.operacionId= f.operacionId

    x.sociedadId = f.sociedadId

    x.usuarioId  = f.usuarioId

    x.lsPartida= new ArrayList<Partida>()


    def ini= true; preasiento.lsPreasientoDetalle.each { println 'preasiento documento cuenta: '+it.cuenta.nombre

    def p= new Partida()

    p.cuenta= ini? f.cuenta : it.cuenta; ini= false

    p.debe  = it.grupo.equalsIgnoreCase('DEBE')?  (it.porcentaje/100) * x.importe : 0

    p.haber = it.grupo.equalsIgnoreCase('HABER')? (it.porcentaje/100) * x.importe : 0

    x.lsPartida.add p }


    return x
  }

}
